<?php
/**
 * EGP Framework
 *
 * LICENSE
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author     Akon(番茄红了) <aultoale@gmail.com>
 * @copyright  Copyright (c) 2008 (http://www.tblog.com.cn)
 * @license    http://www.gnu.org/licenses/gpl.html     GPL 3
 */

!defined('EGP_LIB_DIR') && die('Access Deny!');

/**
 * 支付宝支付 (实物交换/中介担保)
 *
 * @package    classes
 * @author     Akon(番茄红了) <aultoale@gmail.com>
 * @copyright  Copyright (c) 2008 (http://www.tblog.com.cn)
 * @license    http://www.gnu.org/licenses/gpl.html     GPL 3
 */
class EGP_Payment_Adapter_Alipay extends EGP_Payment_Adapter_Abstract
{

    /**
     * 支付方式参数
     *
     * @var array
     */
    protected $_config = array(
        '_input_charset'    => 'utf-8', //参数编码字符集
        'security_code'     => null, //交易安全校验码
        /* 协议参数 */
        'service'           => 'trade_create_by_buyer', //接口名称
        'partner'           => null, //合作伙伴 ID
        'notify_url'        => null, //状态通知 URL
        'return_url'        => null, //支付响应 URL
        'agent'             => null, //代理商 ID
        'sign_type'         => 'MD5', //设定签名加密方式
        /* 业务参数 */
        'subject'           => null, //商名名称
        'body'              => null, //商品描述
        'out_trade_no'      => null, //外部交易号
        'price'             => null, //商品单价
        'discount'          => null, //折扣
        'show_url'          => null, //商品展示网址
        'quantity'          => 1,    //商品购买数量
        'payment_type'      => 1,    //支付类型
        /* 物流信息 */
        'logistics_type'    => null, //物流类型
        'logistics_fee'     => null, //物流费用
        'logistics_payment' => null, //物流支付方式
        'receive_name'      => null, //收货人姓名
        'receive_address'   => null, //收货人地址
        'receive_zip'       => null, //收货人邮政编码
        'receive_phone'     => null, //收货人电话号码
        'receive_mobile'    => null, //收货人手机号码
        /* 卖方信息 */
        'seller_email'      => null, //卖家的支付宝注册 Email
        'seller_id'         => null, //卖家的支付宝注册 ID
        /* 买方信息 */
        'buyer_email'       => null, //买家的支付宝注册 Email
        'buyer_id'          => null, //买家的支付宝注册 ID
        'buyer_msg'         => null, //买家留言
    );

    /**
     * 设置订单编号
     *
     * @param  string  $id
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setId($id)
    {
        if (empty($id)) {
            throw new EGP_Exception('订单编号不允许为空');
        }

        if (strlen($id) > 64) {
            throw new EGP_Exception('订单编号的长度不能大于 64');
        }

        $this->_config['out_trade_no'] = $id;

        return $this;
    }

    /**
     * 设置订单主题
     *
     * @param  string  $subject
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setSubject($subject)
    {
        $subject = str_break(trim(strip_tags((string) $subject)), 256);

        if (empty($subject)) {
            throw new EGP_Exception('商品名称不能为空');
        }

        $this->_config['subject'] = $subject;

        return $this;
    }

    /**
     * 设置订单总金额
     *
     * @param  float  $amount
     * @return EGP_Payment_Adapter_Abstract
     * @throws EGP_Exception
     */
    public function setAmount($amount)
    {
        if (!$this->_isFloat($amount) || floatval($amount) <= 0) {
            throw new EGP_Exception('无效的订单总金额');
        }

        $this->_config['price'] = $amount;
        $this->_config['quantity'] = 1;

        return $this;
    }

    /**
     * 设置参数编码字符集
     *
     * 仅支持 utf-8 / gbk
     *
     * @param  string  $charset
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setInputCharset($charset)
    {
        $charset = strtolower((string) $charset);

        if ($charset != 'utf-8' && $charset != 'gbk') {
            throw new EGP_Exception('无效的参数编码字符集');
        }

        $this->_config['_input_charset'] = $charset;

        return $this;
    }

    /**
     * 设定服务接口名称
     *
     * trade_create_by_buyer : 实物交易
     *
     * @param  string  $service
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setService($service)
    {
        $this->_config['service'] = strtolower((string) $service);

        return $this;
    }

    /**
     * 设置合作伙伴 ID
     *
     * 合作伙伴在支付宝的用户 ID (16个数字字符)
     *
     * @param  string  $partnerId
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setPartner($partner)
    {
        if (empty($partner)) {
            throw new EGP_Exception('合作伙伴 ID 不能为空');
        }

        if (!preg_match('/\d{16}/', $partner)) {
            throw new EGP_Exception('无效的合作伙伴 ID');
        }

        $this->_config['partner'] = $partner;

        return $this;
    }

    /**
     * 设置状态通知 URL
     *
     * 交易状态同步通知接收的 URL
     *
     * @param  string  $url
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setNotifyUrl($url)
    {
        if (!empty($url) && !$this->_isUrl($url)) {
            throw new EGP_Exception('无效的状态通知 URL');
        }

        $this->_config['notify_url'] = (string) $url;

        return $this;
    }

    /**
     * 设置支付响应 URL
     *
     * 处理完成后返回的 URL
     *
     * @param  string  $url
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setReturnUrl($url)
    {
        if (!empty($url) && !$this->_isUrl($url)) {
            throw new EGP_Exception('无效的支付响应 URL');
        }

        $this->_config['return_url'] = (string) $url;

        return $this;
    }

    /**
     * 设置代理商 ID
     *
     * 代理商在支付宝的用户 ID (16个数字字符)
     *
     * @param  string  $agent
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setAgent($agent)
    {
        if (!empty($agent) && !preg_match('/\d{16}/', $agent)) {
            throw new EGP_Exception('无效的代理商 ID');
        }

        $this->_config['agent'] = $agent;

        return $this;
    }

    /**
     * 设置签名加密方式
     *
     * (MD5/DSA) 目前仅支持 MD5
     *
     * @param  string  $type
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setSignType($type)
    {
        $type = strtoupper((string) $type);

        if ($type != 'MD5') {
            throw new EGP_Exception('不支持的签名加密方式');
        }

        $this->_config['sign_type'] = $type;

        return $this;
    }

    /**
     * 设置商品描述
     *
     * @param  string  $body
     * @return EGP_Payment_Adapter_Alipay
     */
    public function setBody($body)
    {
        $this->_config['body'] = str_break((string) $body, 400);

        return $this;
    }

    /**
     * 设置外部交易号
     *
     * 合作伙伴交易号, 唯一
     *
     * @param  string  $no
     * @return EGP_Payment_Adapter_Alipay
     */
    public function setOutTradeNo($no)
    {
        return $this->setId($no);
    }

    /**
     * 设置折扣
     *
     * @param  float  $discount
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setDiscount($discount)
    {
        if ($discount !== '' && $discount !== null) {
            if (!$this->_isFloat($discount) || floatval($discount) < 0) {
                throw new EGP_Exception('无效的商品折扣');
            }

            $this->_config['discount'] = (float) number_format($discount, 2);
        }

        return $this;
    }

    /**
     * 设置商品展示 URL
     *
     * @param  string  $url
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setShowUrl($url)
    {
        if (!empty($url) && !$this->_isUrl($url)) {
            throw new EGP_Exception('无效的支付响应 URL');
        }

        $this->_config['show_url'] = (string) $url;

        return $this;
    }

    /**
     * 设定商品单价
     *
     * @param  float  $price
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setPrice($price)
    {
        if ($price !== '' && $price !== null) {
            if (!$this->_isFloat($price) || floatval($price) <= 0) {
                throw new EGP_Exception('无效的商品单价');
            }

            $this->_config['price'] = $price;
        }

        return $this;
    }

    /**
     * 设置商品购买数量
     *
     * @param  integer  $num
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setQuantity($num)
    {
        $num = (integer) $num;

        if ($num < 1) {
            throw new EGP_Exception('商品购买数量不能小于 1');
        }

        $this->_config['quantity'] = $num;

        return $this;
    }

    /**
     * 设置支付类型
     *
     * @param  string  $type
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setPaymentType($type)
    {
        if (empty($type)) {
            throw new EGP_Exception('支付类型不能为空');
        }

        $type = strval(intval($type));

        if (!array_key_exists($type, $this->getPaymentTypes())) {
            throw new EGP_Exception('不支持的支付类型');
        }

        $this->_config['payment_type'] = $type;

        return $this;
    }

    /**
     * 设置物流类型
     *
     * VIRTUAL : 虚拟物品
     * POST    : 平邮
     * EMS     : EMS
     * EXPRESS : 其他快递公司
     *
     * @param  string  $type
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setLogisticsType($type)
    {
        if (empty($type)) {
            throw new EGP_Exception('实物交易的物流类型不能为空');
        }

        $type = strtoupper((string) $type);

        if (!in_array($type, array('VIRTUAL', 'POST', 'EMS', 'EXPRESS'))) {
            throw new EGP_Exception('无效的物流类型');
        }

        $this->_config['logistics_type'] = $type;

        return $this;
    }

    /**
     * 设置物流费用
     *
     * @param  float  $fee
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setLogisticsFee($fee)
    {
        if ($fee === '' || $fee === null) {
            throw new EGP_Exception('实物交易的物流费用不能为空');
        }

        if (!$this->_isFloat($fee) || floatval($fee) < 0) {
            throw new EGP_Exception('无效的物流费用');
        }

        $this->_config['logistics_fee'] = (float) number_format($fee, 2);

        return $this;
    }

    /**
     * 设置物流支付方式
     *
     * SELLER_PAY              : 卖家支付
     * BUYER_PAY               : 买家支付
     * BUYER_PAY_AFTER_RECEIVE : 货到付款
     *
     * @param  string  $payment
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setLogisticsPayment($payment)
    {
        if (empty($payment)) {
            throw new EGP_Exception('实物交易的物流支付方式不能为空');
        }

        $payment = strtoupper((string) $payment);

        if (!in_array($payment, array('SELLER_PAY', 'BUYER_PAY', 'BUYER_PAY_AFTER_RECEIVE'))) {
            throw new EGP_Exception('无效的物流支付方式');
        }

        $this->_config['logistics_payment'] = $payment;

        return $this;
    }

    /**
     * 设置收货人姓名
     *
     * @param  string  $name
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setReceiveName($name)
    {
        if (!empty($name)) {
            if (strlen($name) > 125) {
                throw new EGP_Exception('收货人姓名不能大于 125 个字符');
            }

            $this->_config['receive_name'] = (string) $name;
        }

        return $this;
    }

    /**
     * 设置收货人地址
     *
     * @param  string  $address
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setReceiveAddress($address)
    {
        if (!empty($address)) {
            if (strlen($address) > 250) {
                throw new EGP_Exception('收货人地址不能大于 250 个字符');
            }

            $this->_config['receive_address'] = (string) $address;
        }

        return $this;
    }

    /**
     * 设置收货人邮政编码
     *
     * @param  string  $zip
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setReceiveZip($zip)
    {
        if (!empty($zip)) {
            if (!$this->_isZip($zip)) {
                throw new EGP_Exception('无效的收货人邮政编码');
            }

            $this->_config['receive_zip'] = (string) $zip;
        }

        return $this;
    }

    /**
     * 设置收货人电话号码
     *
     * @param  string  $phone
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setReceivePhone($phone)
    {
        if (!empty($phone)) {
            if (!$this->_isPhone($phone)) {
                throw new EGP_Exception('无效的收货人电话号码');
            }
            $this->_config['receive_phone'] = (string) $phone;
        }

        return $this;
    }

    /**
     * 设置收货人手机号码
     *
     * @param  string  $mobile
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setReceiveMobile($mobile)
    {
        if (!empty($mobile)) {
            if (!$this->_isMobile($mobile)) {
                throw new EGP_Exception('无效的收货人手机号码');
            }

            $this->_config['receive_mobile'] = (string) $mobile;
        }

        return $this;
    }

    /**
     * 设置卖家的支付宝注册 Email
     *
     * @param  string  $email
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setSellerEmail($email)
    {
        if (!empty($email)) {
            if (strlen($email) <=100 && $this->_isMail($email)) {
                $this->_config['seller_email'] = (string) $email;
            } else {
                throw new EGP_Exception('无效的卖家支付宝注册 Email');
            }
        }

        return $this;
    }

    /**
     * 设置卖家的支付宝注册 ID
     *
     * @param  string  $id
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setSellerId($id)
    {
        if (strlen($id) > 30) {
            throw new EGP_Exception('无效的卖家支付宝注册 ID');
        }

        $this->_config['seller_id'] = (string) $id;

        return $this;
    }

    /**
     * 设置买家的支付宝注册 Email
     *
     * @param  string  $email
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setBuyerEmail($email)
    {
        if (!empty($email)) {
            if (strlen($email) <=100 && $this->_isMail($email)) {
                $this->_config['buyer_email'] = (string) $email;
            } else {
                throw new EGP_Exception('无效的买家支付宝注册 Email');
            }
        }

        return $this;
    }

    /**
     * 设置买家的支付宝注册 ID
     *
     * @param  string  $id
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setBuyerId($id)
    {
        if (strlen($id) > 30) {
            throw new EGP_Exception('无效的买家支付宝注册 ID');
        }

        $this->_config['buyer_id'] = (string) $id;

        return $this;
    }

    /**
     * 设置买家留言
     *
     * @param  string  $message
     * @return EGP_Payment_Adapter_Alipay
     */
    public function setBuyerMsg($message)
    {
        $this->_config['buyer_msg'] = (string) str_break($message, 200);

        return $this;
    }

    /**
     * 设置安全校验码
     *
     * @param  string  $code
     * @return EGP_Payment_Adapter_Alipay
     * @throws EGP_Exception
     */
    public function setSecurityCode($code)
    {
        if (empty($code)) {
            throw new EGP_Exception('安全校验码不能为空');
        }

        if (!is_string($code) || !preg_match('/[a-z0-9]{32}/i', $code)) {
            throw new EGP_Exception('无效的安全校验码');
        }

        $this->_config['security_code'] = (string) $code;

        return $this;
    }

    /**
     * 对参数进行校验
     *
     * @return boolean
     */
    public function isValid()
    {
        $validate = parent::isValid();
        if ($validate && empty($this->_config['seller_email']) && empty($this->_config['seller_id'])) {
            $this->_message = 'seller_email 或 seller_id 至少需要一个参数';
            $validate = false;
        }
        return $validate;
    }

    /**
     * 获取支付网关地址
     *
     * @return string
     */
    public function getGateWay()
    {
        return 'https://www.alipay.com/cooperate/gateway.do';
    }

    /**
     * 获取数字签名
     *
     */
    public function getSign()
    {
        ksort($this->_config);
        reset($this->_config);

        $sign = '';
        foreach ($this->_config as $key => $value) {
            if ($key != 'sign_type' & $key != 'security_code' && $value !== '' && $value !== null) {
                $sign .= ($sign == '' ? '' : '&') . "$key=$value";
            }
        }

        return md5($sign . $this->_config['security_code']);
    }

    /**
     * 获取支付 URL
     *
     * @return string
     */
    public function getPayUrl()
    {
        $sign = $this->getSign();

        $query = '';
        foreach ($this->_config as $key => $value) {
            if ($key != 'sign_type' && $key != 'security_code' && $value !== '' && $value !== null) {
                $query .= ($query == '' ? '' : '&') . $key . '=' . urlencode($value);
            }
        }

        return $this->getGateWay() . '?' . $query . '&sign=' . $this->getSign() . '&sign_type=MD5';
    }

    /**
     * 获取可用的支付类型
     *
     * @return array
     */
    public function getPaymentTypes()
    {
        return array(
            '1' => '商品购买',
            '2' => '服务购买',
            '3' => '网络拍卖',
            '4' => '捐赠',
            '5' => '邮费补偿',
            '6' => '奖金',
        );
    }

}